#include "newdialog.h"
#include "ui_newdialog.h"
#include <QJsonObject>
#include <QJsonDocument>
#include <QCryptographicHash>
NewDialog::NewDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::NewDialog)
{
    ui->setupUi(this);


    ui->chiose_button->setEnabled(false);
    ui->chiose_button->setVisible(false);
    ui->sendupdata_button->setEnabled(false);
    ui->sendupdata_button->setVisible(false);
}

NewDialog::~NewDialog()
{
    delete ui;
}

void NewDialog::on_search_button_clicked() //设备搜索
{
    udpServer = new QUdpSocket(this);


    if(!udpServer->bind(QHostAddress::Any, 8888))
    {
        QMessageBox::warning(this,"提示","服务器启动失败");
        return;
    }
    ui->textBrowser->setText("启动成功");
    ui->textBrowser->append("寻找设备中...");

    QJsonObject json;//创建JSON对象
    json["type"]=0;
    json["request"]="connect_info";

    QJsonDocument doc(json);//将JSON转化为字节数组
    QByteArray datagram =doc.toJson(QJsonDocument::Compact);
    QHostAddress broadcastAddress = QHostAddress::Broadcast; // 255.255.255.255
    quint16 port = 8888;  // 目标端口

    // 发送广播
    qint64 sent = udpServer->writeDatagram(datagram, broadcastAddress, port);

    if(sent == -1)
    {
        qDebug() << "发送失败:" << udpServer->errorString();
    }
    else
    {
        qDebug() << "广播成功:" << broadcastAddress.toString() << "端口:" << port;

        if(udpServer->waitForReadyRead(5000))
        {
            ui->textBrowser->append("找到设备:");
            get_recv(udpServer);
        }
        else
        {
            ui->textBrowser->append("没找到设备");
        }
    }
}
void NewDialog::on_connect_button_clicked()//连接
{
    if (ui->comboBox->count() == 0) {
        QMessageBox::warning(this, "警告", "请先搜索设备");
        return;
    }

    QString fullAddress = ui->comboBox->currentData().toString();
    qDebug() << "选中的完整地址:" << fullAddress;

    // 分割IP和端口
    QStringList parts = fullAddress.split(":");
    if (parts.size() == 2)
    {
        QString ip = parts[0];
        addr_updata=ip;
        quint16 port = parts[1].toUShort();
        QHostAddress address;
        address.setAddress(ip);
        qDebug() << "IP:" << ip << "端口:" << port;



        //建立tcp连接问询设备是否需要升级
        tcpServer= new QTcpSocket(this);
        tcpServer->connectToHost(QHostAddress(addr_updata),8889);
        if (tcpServer->waitForConnected(1000))//判断tcp连接是否建立
        {
            ui->textBrowser->append("连接成功...");
            ui->chiose_button->setEnabled(true);
            ui->chiose_button->setVisible(true);
        }

    }
    else
    {
        QMessageBox::warning(this, "错误", "无效的设备地址格式");
    }

}

void NewDialog::on_chiose_button_clicked()//选择升级包
{
    if (tcpServer->waitForConnected(1000))//判断tcp连接是否建立
    {
        // 开文件选择窗选文件，返回带路径文件名
        fileName = QFileDialog::getOpenFileName(this);
        if(!fileName.isEmpty())
        {
            //打开文件并获取文件信息
            readFile.setFileName(fileName);
            if (!readFile.open(QFile::ReadOnly))
            {
                qDebug() << "无法打开文件";
                return;
            }
            else
            {
                ui->textBrowser->append(QString("File %1 Opened!").arg(fileName));
                // 获取文件名、大小、md5码
                QFileInfo fileInfo(fileName);
                QString name = fileInfo.fileName();
                qint64 size = fileInfo.size();
                // 计算文件的 MD5 码
                QCryptographicHash hash(QCryptographicHash::Md5);
                QString md5String;
                QByteArray fileData = readFile.readAll();
                hash.addData(fileData); // 直接调用 addData 函数
                QByteArray md5Bytes = hash.result(); // 获取哈希结果
                md5String = QString::fromUtf8(md5Bytes.toHex()); // 将 MD5 字节数组转换为 QString

                qDebug() << "文件名:" << name;
                qDebug() << "文件大小:" << size << "字节";
                qDebug() << "MD5 码:" << md5String;

                //升级询问包转JSON格式
                QJsonObject rootObject;
                rootObject["type"] = 1;
                QJsonObject dataObject;

                dataObject["ver"] = "v1.0.1";           // 版本号
                dataObject["file_name"] = name; // 文件名
                dataObject["file_len"] = size;        // 文件大小（字节）
                dataObject["md5"] = md5String;     // MD5校验码
                rootObject["data"] = dataObject;
                QJsonDocument jsonDoc(rootObject);
                QByteArray updatajson = jsonDoc.toJson(QJsonDocument::Indented);
                tcpServer->write(updatajson);//问询设备是否需要升级
                connect(tcpServer,SIGNAL(readyRead()),this,SLOT(readSlot()));
            }
        }
        else
        {
            ui->textBrowser->setText(QString("*** FAIL OPENING FILE"));
        }
    }
}
void NewDialog::get_recv(QUdpSocket *udpServer)//收设备的udp回复
{
    while (udpServer->hasPendingDatagrams())
    {  //如果缓冲区有至少一个数据包，那么返回真
        QByteArray datagram;
        datagram.resize(udpServer->pendingDatagramSize());  //重设接收缓冲区大小为待读取的字节数
        QHostAddress sender;    // 存储发送方IP
        quint16 senderPort;     // 存储发送方端口

        udpServer->readDatagram(datagram.data(), datagram.size(),
                                &sender, &senderPort);//接收消息

        // 将数据转为字符串
        QString fullMessage = QString::fromUtf8(datagram);
        if(fullMessage=="connect_info_ok")
        {
            qDebug()<<"收到回复啦"<<endl;
            ui->textBrowser->append(sender.toString());
            addDatatoComBox(sender.toString(),senderPort);
        }


    }
}

void NewDialog::addDatatoComBox(const QString &ipAddress,quint16 senderPort)//设置设备的下拉列表
{
    // 创建包含IP和端口的显示文本
    QString displayText = QString("%1:%2").arg(ipAddress).arg(senderPort);

    // 检查是否已存在
    for(int i = 0; i < ui->comboBox->count(); i++)
    {
        if (ui->comboBox->itemText(i) == displayText)
        {
            return; // 已存在则直接返回
        }
    }

    // 添加选项，存储完整地址信息
    ui->comboBox->addItem(displayText, QVariant(ipAddress + ":" + QString::number(senderPort)));
    qDebug() << "已添加设备:" << displayText;
}
void NewDialog::readSlot()//接收设备是否升级回复
{
    QByteArray frame = tcpServer->readAll();

    //用什么编码转回来，要看发过来的字节流的编码
    QString str = QString::fromLocal8Bit(frame);
    //建立一路新的tcp连接用来传输升级数据包
    tcp_updata=new QTcpSocket(this);

    tcp_updata->connectToHost(QHostAddress(addr_updata), 12345);
    if(tcp_updata->waitForConnected(1000))
    {
        ui->textBrowser->append("升级包连接已建立...");

    }
    if(str=="need_updata")
    {

        ui->textBrowser->append("设备需要升级");
        ui->sendupdata_button->setEnabled(true);
        ui->sendupdata_button->setVisible(true);

    }
    else if(str=="notneed_updata")
    {
        ui->textBrowser->append("设备已是最新版本");
    }
    if(str=="updata_false")
    {
        ui->textBrowser->append("升级文件传输失败");
    }
    else if(str=="updata_success")
    {
        ui->textBrowser->append("升级文件传输成功");
    }
}
void NewDialog::start_transfer()//文件传输
{
    if (!readFile.isOpen() || !tcp_updata || tcp_updata->state() != QAbstractSocket::ConnectedState) {
        QMessageBox::warning(this, "警告", "请先选择文件并建立连接");
        return;
    }

    //创建文件流对象
    QFile readFile(fileName);
    // 打开文件流
    readFile.open(QIODevice::ReadOnly); // 只读模式
    // 添加进度条效果
    qint64 totalSize = readFile.size();    // 获得文件总大小
    qint64 hasRead = 0; //  已经读取的大小
    qint64 writeRet=0;//本次读取大小
    QByteArray array;   // 字节数组类

    while(!readFile.atEnd()) // 判断是否读到文件尾部
    {

        array = readFile.read(32);
        writeRet = tcp_updata->write(array);
        if(writeRet<0)
        {
            // 关闭数据流
            readFile.close();
            ui->textBrowser->append("安装包发送失败");

        }
        hasRead += writeRet;    // 获取已经写出的总大小
        int per = hasRead *100 / totalSize;  // 计算百分比
        ui->progressBar->setValue(per); // 设置到进度条

    }

    //ui->revTextBrowser->append("压缩包传输完成");

}




void NewDialog::on_sendupdata_button_clicked()//发送升级包
{
    start_transfer();
}
